﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Collections.Specialized;
using System.Xml;
using System.IO;
using System.Drawing;

namespace Snom
{
    class CommandsSender
    {
        private Browser _browser;

        public CommandsSender(string phoneIpAddress, string username, string password, Uri proxyUri)
        {
            PhoneIpAddress = phoneIpAddress;
            Username = username;
            Password = password;
            ProxyUri = proxyUri;
            _browser = new Browser(ProxyUri);
        }

        public string PhoneIpAddress
        {
            get;
            private set;
        }

        public string Username
        {
            get;
            private set;
        }

        public string Password
        {
            get;
            private set;
        }

        public bool UseCredentials
        {
            get
            {
                return !string.IsNullOrEmpty(Username) || !string.IsNullOrEmpty(Password);
            }
        }

        public Uri ProxyUri
        {
            get;
            set;
        }

        /// <summary>
        /// http://wiki.snom.com/FAQ/Can_I_control_my_snom_phone_remotely
        /// </summary>
        public void ExecuteCommandSequence(string commandSequence)
        {
            int index = 0;
            while (index < commandSequence.Length)
            {
                char c = commandSequence[index];

                if (c.Equals('{'))
                {
                    int end = commandSequence.IndexOf("}", index);

                    if (end < 0)
                    {
                        throw new Exception(string.Format("unclosed {} index {0}", index));
                    }

                    string command = commandSequence.Substring(index + 1, end - index - 1);

                    string[] operands = command.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    string commandName = operands[0];
                    string commandParameter = null;

                    if (operands.Length > 1)
                    {
                        commandParameter = operands[1];
                    }

                    if (commandName.Equals("Wait", StringComparison.InvariantCultureIgnoreCase))
                    {
                        int ms = (int)(double.Parse(commandParameter) * 1000.0);
                        Thread.Sleep(ms);
                    }

                    else if (commandName.Equals("Dial", StringComparison.InvariantCultureIgnoreCase))
                    {
                        DialNumber(commandParameter);
                    }

                    else if (commandName.Equals("Dtmf", StringComparison.InvariantCultureIgnoreCase))
                    {
                        SendDtmfKey(commandParameter);
                    }

                    else
                    {
                        string keyCommand = SnomKeys.KeyValueFromKeyName(commandName);

                        if (keyCommand != null)
                        {
                            SendKey(keyCommand);
                        }
                    }

                    index = end + 1;

                    continue;
                }
                else
                {
                    throw new Exception("Illegal charcter in sequence: " + commandSequence.Substring(0, index));
                }
            }
        }

        private string GetCommandUrl(string action)
        {
            return string.Format("{0}/command.htm?{1}", GetBaseUrl(), action);
        }

        public void SendKey(string keyName)
        {
            _browser.ExecutePhoneRequestNoResponse(GetCommandUrl(string.Format("key={0}", keyName)));
        }

        /// <summary>
        /// use 0-9, %23 for # and * for star
        /// {Dtmf0}
        /// </summary>
        /// <param name="dtmfKeys"></param>
        public void SendDtmfKey(string dtmfKeys)
        {
            _browser.ExecutePhoneRequestNoResponse(GetCommandUrl(string.Format("key_dtmf={0}", ToSendableDigits(dtmfKeys))));
        }

        public void DialNumber(string number)
        {
            _browser.ExecutePhoneRequestNoResponse(GetCommandUrl(string.Format("number={0}", number)));
        }

        private bool IsLegalDigit(char digit)
        {
            return (digit >= '0' && digit <= '9') || digit == '#' || digit == '*';
        }

        private string ToSendableDigits(string digits)
        {
            foreach (char c in digits)
            {
                if (!IsLegalDigit(c))
                {
                    throw new Exception("Illegal digit: " + c);
                }
            }

            return digits.Replace("#", SnomKeys.KeySharp);
        }

        public void ChangeSetting(string name, string value)
        {
            string url = string.Format("{0}/dummy.htm?settings=save&{1}={2}", GetBaseUrl(), name, value);
            _browser.ExecutePhoneRequestNoResponse(url);
        }

        public NameValueCollection GetSettings()
        {
            string url = string.Format("{0}/settings.xml", GetBaseUrl());

            string content = _browser.GetContent(url);
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(content);

            XmlNode settingsRootNode = xml.DocumentElement.FirstChild;
            NameValueCollection result = new NameValueCollection();

            foreach (XmlNode settingNode in settingsRootNode.ChildNodes)
            {
                string name = settingNode.Name;
                XmlAttribute idxAttribute = settingNode.Attributes["idx"];
                if (idxAttribute != null)
                {
                    name += idxAttribute.Value;
                }

                result.Add(name, settingNode.InnerText);
            }

            return result;
        }

        public Image GetScreenDisplayImage()
        {
            string url = string.Format("{0}/screen.bmp", GetBaseUrl());

            using (HttpWebResponse response = _browser.ExecuteHttpGetRequest(url))
            using (Stream stream = response.GetResponseStream())
            {
                return Image.FromStream(stream);
            }
        }

        private string GetBaseUrl()
        {
            if (UseCredentials)
            {
                return string.Format("http://{0}:{1}@{2}", PhoneIpAddress, Username, Password);
            }

            return string.Format("http://{0}", PhoneIpAddress);
        }
    }
}
